<!DOCTYPE html>
<html lang="en">
<head>
    <!-- Use correct character set. -->
    <meta charset="utf-8">
    <!-- Tell IE to use the latest, best version. -->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <!-- Make the application on mobile take up the full browser screen and disable user scaling. -->
    <meta name="viewport"
          content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
    <title>Hello World!</title>
    <script src="node_modules/cesium/Build/Cesium/Cesium.js"></script>
    <script src="node_modules/jquery/dist/jquery.js"></script>
    <script src="node_modules/bootstrap/dist/js/bootstrap.js"></script>
    <script src="src/js/setting.js"></script>
    <style>
        @import url(node_modules/cesium/Build/Cesium/Widgets/widgets.css);
        @import url(src/css/main.css);
        @import url(node_modules/bootstrap/dist/css/bootstrap.css);
    </style>
</head>
<body>
<input id="input_level" placeholder="层级">
<input id="input_height_min" placeholder="最小高度">
<input id="input_height_max" placeholder="最大高度">
<button id="btn_draw">Draw</button>
<div id="cesiumContainer"></div>
<script>
    Cesium.Ion.defaultAccessToken = SETTING.AccessToken;
    var viewer;


    function encode(level, lon, lat, height) {
        let lonlatResolution = LON_LAT_TABLE[level];
        let heightResolution = HEIGHT_TABLE[level];
        let lonInt = parseInt(Math.abs(lon) / lonlatResolution);
        let latInt = parseInt(Math.abs(lat) / lonlatResolution);
        let heightInt = parseInt(Math.abs(height) / heightResolution);
        console.assert(lonInt <= Math.pow(2, level - 1));
        console.assert(latInt <= Math.pow(2, level - 1));
        console.assert(heightInt <= Math.pow(2, level - 1));
        if (heightInt > Math.pow(2, level - 1))
            console.log(height);
        let lonBinS = lonInt.toString(2);
        let latBinS = latInt.toString(2);
        let heightBinS = heightInt.toString(2);
        console.assert(lonBinS.length <= level - 1);
        console.assert(latBinS.length <= level - 1);
        console.assert(heightBinS.length <= level - 1);

        let lonBin = Array(level - lonBinS.length).join('0') + lonBinS;
        let latBin = Array(level - latBinS.length).join('0') + latBinS;
        let heightBin = Array(level - heightBinS.length).join('0') + heightBinS;
        lonBin = lon >= 0 ? '1' + lonBin : '0' + lonBin;
        latBin = lat >= 0 ? '1' + latBin : '0' + latBin;
        heightBin = height >= 0 ? '1' + heightBin : '0' + heightBin;
        let codeArray = Array();
        console.assert(lonBin.length === latBin.length && latBin.length === heightBin.length);
        for (var i = 0; i < lonBin.length; i++) {
            codeArray.push(lonBin[i]);
            codeArray.push(latBin[i]);
            codeArray.push(heightBin[i]);
        }
        return codeArray.join('');
    }


    function get_deg_min_sec(degree) {
        degree = Math.abs(degree);
        let deg = parseInt(degree);
        let minute = parseInt(degree * 60 - deg * 60);
        let second = degree * 3600 - deg * 3600 - minute * 60;
        return [deg, minute, second];
    }


    function encode_geosot_3d(level, lon, lat, height) {
        let lon_lat_resolution = LON_LAT_TABLE[level];
        let height_resolution = HEIGHT_TABLE[level];
        let lon_deg_min_sec = get_deg_min_sec(lon);
        let lat_deg_min_sec = get_deg_min_sec(lat);

        let lon_deg_bin = lon_deg_min_sec[0].toString(2);
        let lat_deg_bin = lat_deg_min_sec[0].toString(2);
        lon_deg_bin = Array(9 - lon_deg_bin.length).join('0') + lon_deg_bin;
        lat_deg_bin = Array(9 - lat_deg_bin.length).join('0') + lat_deg_bin;

        let lon_min_bin = lon_deg_min_sec[1].toString(2);
        let lat_min_bin = lat_deg_min_sec[1].toString(2);
        lon_min_bin = Array(7 - lon_min_bin.length).join('0') + lon_min_bin;
        lat_min_bin = Array(7 - lat_min_bin.length).join('0') + lat_min_bin;

        let lon_sec_bin = parseInt(lon_deg_min_sec[2] / 3600 / lon_lat_resolution).toString(2);
        let lat_sec_bin = parseInt(lon_deg_min_sec[2] / 3600 / lon_lat_resolution).toString(2);
        lon_sec_bin = Array(17 - lon_sec_bin.length).join('0') + lon_sec_bin;
        lat_sec_bin = Array(17 - lat_sec_bin.length).join('0') + lat_sec_bin;

        let lon_bin = lon_deg_bin.substr(0, 9) + lon_min_bin.substr(0, 7) + lon_sec_bin;
        let lat_bin = lat_deg_bin.substr(0, 9) + lat_min_bin.substr(0, 7) + lat_sec_bin;
        let height_bin = parseInt(Math.abs(height) / height_resolution).toString(2);
        height_bin = Array(level - height_bin.length).join('0') + height_bin;

        let lon_cap = lon >= 0 ? '1' : '0';
        let lat_cap = lat >= 0 ? '1' : '0';
        let height_cap = height >= 0 ? '1' : '0';

        lon_bin = lon_cap + lon_bin;
        lat_bin = lat_cap + lat_bin;
        height_bin = height_cap + height_bin;
        let code_array = Array();
        for (var i = 0; i < level; i++) {
            code_array.push(lon_bin[i]);
            code_array.push(lat_bin[i]);
            code_array.push(height_bin[i]);
        }
        return code_array.join('');
    }

    function encode_geosot_2d(level, lon, lat) {
        let lon_lat_resolution = LON_LAT_TABLE[level];
        let lon_deg_min_sec = get_deg_min_sec(lon);
        let lat_deg_min_sec = get_deg_min_sec(lat);

        let lon_deg_bin = lon_deg_min_sec[0].toString(2);
        let lat_deg_bin = lat_deg_min_sec[0].toString(2);
        lon_deg_bin = Array(9 - lon_deg_bin.length).join('0') + lon_deg_bin;
        lat_deg_bin = Array(9 - lat_deg_bin.length).join('0') + lat_deg_bin;

        let lon_min_bin = lon_deg_min_sec[1].toString(2);
        let lat_min_bin = lat_deg_min_sec[1].toString(2);
        lon_min_bin = Array(7 - lon_min_bin.length).join('0') + lon_min_bin;
        lat_min_bin = Array(7 - lat_min_bin.length).join('0') + lat_min_bin;

        let lon_sec_bin = parseInt(lon_deg_min_sec[2] / 3600 / lon_lat_resolution).toString(2);
        let lat_sec_bin = parseInt(lon_deg_min_sec[2] / 3600 / lon_lat_resolution).toString(2);
        lon_sec_bin = Array(17 - lon_sec_bin.length).join('0') + lon_sec_bin;
        lat_sec_bin = Array(17 - lat_sec_bin.length).join('0') + lat_sec_bin;

        let lon_bin = lon_deg_bin.substr(0, 9) + lon_min_bin.substr(0, 7) + lon_sec_bin;
        let lat_bin = lat_deg_bin.substr(0, 9) + lat_min_bin.substr(0, 7) + lat_sec_bin;

        let lon_cap = lon >= 0 ? '1' : '0';
        let lat_cap = lat >= 0 ? '1' : '0';

        lon_bin = lon_cap + lon_bin;
        lat_bin = lat_cap + lat_bin;
        let code_array_bin = Array();
        let code_array_quad = Array();
        for (var i = 0; i < level; i++) {
            code_array_bin.push(lon_bin[i]);
            code_array_bin.push(lat_bin[i]);
            code_array_quad.push(parseInt(lon_bin[i] * 2) + parseInt(lat_bin[i]));
        }
        return code_array_quad.join('');
    }

    function decode_geosot(code) {

    }

    /**
     *
     * @param code
     * @returns {{level: number, min_coord: number[], max_coord: *[]}}
     */
    function decode(code) {
        console.assert(code.length % 3 === 0 && code.length > 3);
        let level = code.length / 3;
        let lonArray = Array();
        let latArray = Array();
        let heightArray = Array();
        for (var i = 3; i < code.length; i++) {
            if (i % 3 === 0) {
                lonArray.push(code[i]);
            }
            else if (i % 3 === 1) {
                latArray.push(code[i]);
            }
            else {
                heightArray.push(code[i]);
            }
        }
        let lonString = lonArray.join('');
        let latString = latArray.join('');
        let heightString = heightArray.join('');
        let lonInt = parseInt(lonString, 2);
        let latInt = parseInt(latString, 2);
        let heightInt = parseInt(heightString, 2);
        let lonlatResolution = LON_LAT_TABLE[level];
        let heightResolution = HEIGHT_TABLE[level];
        let lonMin = lonInt * lonlatResolution;
        let latMin = latInt * lonlatResolution;
        let heightMin = heightInt * heightResolution;
        lonMin = code[0] === '1' ? lonMin : -lonMin;
        latMin = code[1] === '1' ? latMin : -latMin;
        heightMin = code[2] === '1' ? heightMin : -heightMin;
        return {
            level: level,
            min_coord: [lonMin, latMin, heightMin],
            max_coord: [lonMin + lonlatResolution, latMin + lonlatResolution, heightMin + heightResolution]
        }

    }

    function draw_code(viewer, code, style) {
        let codeRange = decode(code);
        let level = codeRange.level;
        console.log(codeRange);
        if (codeRange.min_coord[0] >= 180 || codeRange.max_coord[0] <= -180 ||
            codeRange.min_coord[1] >= 90 || codeRange.max_coord[1] <= -90)
            return;
        let lon_min = codeRange.min_coord[0] < -180 ? -179.999 : codeRange.min_coord[0];
        let lat_min = codeRange.min_coord[1] < -90 ? -89.999 : codeRange.min_coord[1];
        let lon_max = codeRange.max_coord[0] > 180 ? 179.999 : codeRange.max_coord[0];
        let lat_max = codeRange.max_coord[1] > 90 ? 89.999 : codeRange.max_coord[1];
        console.log(lon_min, lat_min, lon_max, lat_max);

        viewer.entities.add({
            name:
            code,
            rectangle: {
                coordinates: Cesium.Rectangle.fromDegrees(lon_min, lat_min, lon_max, lat_max),
                material: Cesium.Color.WHITE.withAlpha(0.1),
                extrudedHeight: HEIGHT_TABLE[level],
                height: codeRange.min_coord[2],
                outline: true,
                outlineColor: Cesium.Color.YELLOW
            }
        });
    }

    function draw_codes(viewer, level, lon_range, lat_range, height_range) {
        let lonlatResolution = LON_LAT_TABLE[level];
        let heightResolution = HEIGHT_TABLE[level];
        let lon_min = lon_range ? lon_range[0] : -180;
        let lon_max = lon_range ? lon_range[1] : 180;
        let lat_min = lon_range ? lat_range[0] : -90;
        let lat_max = lon_range ? lat_range[1] : 90;
        let height_min = lon_range ? height_range[0] : 0;
        let height_max = lon_range ? height_range[1] : Math.pow(2, 16);

        console.log("long lat resolution", lonlatResolution);
        console.log("height resolution", heightResolution);

        for (var lon = lon_min; lon < lon_max; lon += lonlatResolution) {
            for (var lat = lat_min; lat < lat_max; lat += lonlatResolution) {
                for (var height = height_min; height < height_max; height += heightResolution) {
                    let code = encode(level,
                        lon + lonlatResolution / 2,
                        lat + lonlatResolution / 2,
                        height + heightResolution / 2);
                    draw_code(viewer, code);
                }

            }
        }
    }

    $(document).ready(function () {

        viewer = new Cesium.Viewer('cesiumContainer', {
            animation: false,
            baseLayerPicker: true,
            fullscreenButton: true,
            geocoder: true,
            timeline: false,
            vrButton: false,
        });
        viewer.canvas.addEventListener('click', function (e) {
            var level = $("#input_level").val();
            var mousePosition = new Cesium.Cartesian2(e.clientX, e.clientY);

            var ellipsoid = viewer.scene.globe.ellipsoid;
            var cartesian = viewer.camera.pickEllipsoid(mousePosition, ellipsoid);
            if (cartesian) {
                var cartographic = ellipsoid.cartesianToCartographic(cartesian);
                var longitude = Cesium.Math.toDegrees(cartographic.longitude);
                var latitude = Cesium.Math.toDegrees(cartographic.latitude);
                var code = encode(level,
                    longitude,
                    latitude,
                    HEIGHT_TABLE[level] / 2);
                draw_code(viewer, code);
            } else {
                alert('Globe was not picked');
            }

        }, false);
        // var provider = Cesium.createOpenStreetMapImageryProvider({
        //     url : 'http://localhost:8080/'
        // });
        //
        // viewer.imageryLayers.addImageryProvider(provider);
        $("#btn_draw").click(function () {
            let level = $("#input_level").val();
            let heightMin = parseInt($("#input_height_min").val());
            let heightMax = parseInt($("#input_height_max").val());
            let ellipsoid = viewer.scene.globe.ellipsoid;
            let car3_min = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(0, viewer.canvas.height), ellipsoid);
            let car3_max = viewer.camera.pickEllipsoid(new Cesium.Cartesian2(viewer.canvas.width, 0), ellipsoid);
            car3_min = ellipsoid.cartesianToCartographic(car3_min);
            car3_max = ellipsoid.cartesianToCartographic(car3_max);
            let lon_min = Cesium.Math.toDegrees(car3_min.longitude);
            let lon_max = Cesium.Math.toDegrees(car3_max.longitude);
            let lat_min = Cesium.Math.toDegrees(car3_min.latitude);
            let lat_max = Cesium.Math.toDegrees(car3_max.latitude);
            console.log(lon_min,lat_min,lon_max,lat_max);
            draw_codes(viewer, level, [lon_min, lon_max], [lat_min, lat_max], [heightMin, heightMax]);
        });
    });

</script>
</body>
</html>

